/**
 * instanceof的工作方式 A instanceof B
 * 判断对象A的原型链（A.__proto__组成的原型链）中是否包含函数B的原型对象（B.prototype）
 * 工作方式：
 *
 */

/**
 * 原型，构造函数，实例三者之间的关系
 * 构造函数Person：它有一个属性指向原型对象，即Person.prototype
 * 原型Person.prototype: 它有一个默认的函数指向构造函数，即Person.prototype.constructor
 * 实例对象：内部有一个私有属性person[__proto__]指向原型对象
 * 踪上所述，在查找实例对象的属性时，
 * 第一：会先查找自身是否存在这个属性，如果有直接返回，否则进行第二步。
 * 第二：会在实例对象的person[__proto__]原型对象上继续查找，如果有返回，否则进行第三步。
 * 第三：继续在原型对象的原型上继续查找，重复第二步，直到查找到Object.prototype原型对象上
 * 注意：当实例对象上没有的属性，在原型对象上存在时，此时对实例对象的这个属性赋值不会影响到
 * 原型对象上的属性，二十给这个实例对象新增一个属性！
 * 如：Person.prototype = { name: 'zhangsan'}，然后person.name = 'lisi'
 * 这时不会修改原型上的name属性，而是给person实例新增一个属性name。
 */

/**
 * js继承的几种方式
 * 1 原型链继承
 * 缺点：
 * 第一：当原型有引用数据类型时，子类的实例修改yi下，就会影响到所有的子类实例
 * 第二：子类在实例化时，不能够给父类的构造函数传递参数（因为原型在定义子类时就已经实例化完成了）
 * 2 借用构造函数继承
 * 缺点：
 * 第一：无法使用到父类原型上的数学和方法。
 * 第二：每次子类实例化，都需要允许一遍父类的构造函数。
 * 3 组合式继承
 * 即结合了原型继承和借用构造函数继承的方式，弥补了两者单独使用的缺点
 * 缺点：
 * 1 这里调用了两次父类的构造函数
 * 2 子类的原型上会多出父类实例的属性（多余）
 * 
 * 4 原型式继承
 *  作用：就是以目标对象为原型，创建出一个新的对象
 * 
 * 5 寄生式继承（使用到原型式继承）
 *  作用：增强一个对象的属性和方法
 *  缺点：只能继承父类的原型上的方法和属性，实例属性无法继承
 * 6 寄生式组合类型（借用构造函数+寄生式继承）
 *  这就是babel中使用的方式实现继承
 *  作用：解决组合式继承构造函数调用两次导致属性重复的问题。
 *  解决了寄生式继承和借用构造函数继承的缺点。
 */

// 原型链继承
function SuperType(value) {
  this.value = value;
  this.colors = ['1', '2', '3'];
}
SuperType.prototype.getSuperValue = function () {
  console.log(this.value);
};

function SubType(name) {
  this.name = name;
}
SubType.prototype = new SuperType();
SubType.prototype.getName = function () {
  console.log('subtype: ' + this.name);
};

const instance1 = new SubType();
instance1.colors.push('4');
const instance2 = new SubType();
// 下面输出的都是一样的数组对象，都被影响了
console.log(instance1.colors);
console.log(instance2.colors);
console.log(instance2 instanceof SubType);
console.log(SubType.prototype.constructor);

// 借用构造函数继承
function SubType2(name) {
  SuperType.call(this, 'value');
  this.name = name;
}

// 组合继承
function SubType3(value, name) {
  // 解决了原型继承不能在子类实例化传参以及原型属性是引用类型导致的共享同一个引用的问题
  SuperType.call(this, value);
  this.name = name;
}
// 这个解决了借用构造函数无法使用父类的原型上的属性和方法问题
SubType3.prototype = new SuperType();
SubType3.prototype.say = function () {
  console.log(this.name);
};

// 原型式继承，即传入一个对象，以这个对象最为原型，返回一个新的对象，类似Object.create(o, properties)
function CreateObject(o) {
  function F() {};
  F.prototype = o;
  return new F();
}

// 寄生式继承：作用就是增强一个对象（添加属性或者方法，配合原型式继承）
function createAnother(o) {
  const obj = CreateObject(o);
  // 添加属性或者方法
  obj.say = function () {
    console.log('say');
  }
  return obj;
}

// 寄生式组合类型
function inherit(SubType, SuperType) {
  const prototype = CreateObject(SuperType.prototype);
  prototype.constructor = SubType;
  SubType.prototype = prototype;
}
function SubType4(value, name) {
  SuperType.call(this, value);
  this.name = name;
}
inherit(SubType4, SuperType);
SubType4.prototype.getName = function () {
  return this.name;
}